How to Create Authentic Economist Style Charts in R (Advanced)

Data Visualization
R
ggplot2
Advanced
A deep dive into recreating The Economist’s exact visual style, including the signature red header tag and custom grid styling, without relying on pre-built themes.
Author

Te Guo

Published

February 8, 2026

In a previous attempt, we used ggthemes, which is great for a quick start. But to truly replicate The Economist, we need the signature red rectangle (tag) in the top-left corner and precise control over the grid and typography.

Here is how to do it “the hard way” — manually building the theme and using annotation_custom to embed the red tag directly into the plot.

1. Setup: Libraries and Custom Theme (设置:加载包和自定义主题)

We will define a custom theme function and a special function to add the red tag.

# 加载必要的包
# Load necessary packages
library(ggplot2)
library(dplyr)
library(grid)

# 定义 Economist 风格颜色
# Define Economist colors
econ_bg   <- "#d5e4eb"   # 背景蓝灰色 (Blue-gray background)
econ_grid <- "#ffffff"   # 白色网格 (White grid)
econ_red  <- "#ed1b24"   # 标志性红色 (Iconic Red)
econ_blue <- "#014d64"   # 深蓝色线条 (Dark blue lines)

# 自定义主题函数
# Custom Theme Function
theme_economist_manual <- function() {
  theme_minimal(base_size = 14) +
    theme(
      # 背景颜色 (Background color)
      plot.background  = element_rect(fill = econ_bg, color = NA),
      panel.background = element_rect(fill = econ_bg, color = NA),
      
      # 网格线:只保留水平白线 (Grid lines: horizontal white only)
      panel.grid.major.y = element_line(color = econ_grid, size = 1.2),
      panel.grid.major.x = element_blank(),
      panel.grid.minor = element_blank(),
      
      # 增加顶部边距,给红标留位置 (Add top margin for the red tag)
      plot.margin = margin(t = 2, r = 1, b = 1, l = 1, unit = "cm"),

      # 文字排版 (Typography)
      plot.title    = element_text(face = "bold", size = 16, hjust = 0, margin = margin(b = 5)),
      plot.subtitle = element_text(size = 12, hjust = 0, margin = margin(b = 15)),
      plot.caption  = element_text(size = 9, hjust = 0, color = "#555555", margin = margin(t = 10)),
      axis.title    = element_blank(),  # 隐藏轴标题 (Hide axis titles)
      axis.text     = element_text(color = "#333333", size = 10),
      
      # 图例 (Legend)
      legend.position = "top",
      legend.justification = "left",
      legend.background = element_blank(),
      legend.key = element_blank()
    )
}

# 黑科技:使用 annotation_custom 添加红标
# Advanced: Use annotation_custom to add the red tag
# 这会将红标作为图表的一部分,确保它不会丢失 (Ensures tag is part of the plot object)
add_economist_tag <- function() {
  annotation_custom(
    grob = rectGrob(gp = gpar(fill = econ_red, col = NA)), 
    xmin = -Inf, xmax = Inf,   # Stretch across x (controlled by grob width usually, but here we place it manually)
    ymin = Inf,  ymax = Inf    # Place at top
  )
}

# 更好的红标方法:直接在坐标系外绘制
# A robust function to add the tag
with_red_tag <- function(p) {
  # 使用 coord_cartesian(clip = "off") 允许在绘图区外绘制
  # Use clip = "off" to draw outside the panel
  p + 
    coord_cartesian(clip = "off") +
    theme(plot.margin = margin(t = 1.5, r = 0.5, b = 0.5, l = 0.5, "cm")) +
    annotation_custom(
      grob = rectGrob(gp = gpar(fill = econ_red, col = NA), width = unit(1.5, "cm"), height = unit(0.8, "cm"), hjust = 0, vjust = 1),
      xmin = -Inf, xmax = -Inf, # Anchor to top-left
      ymin = Inf, ymax = Inf
    )
}

2. Line Chart: Inflation Rate (折线图)

Let’s visualize inflation data. We use annotation_custom to place the red block.

# 1. 准备数据
# Prepare data
data(economics)
df_line <- economics %>% filter(date > "2000-01-01")

# 2. 基础绘图 + 红标
# Plot with Red Tag
p1 <- ggplot(df_line, aes(x = date, y = uempmed)) +
  geom_line(color = econ_blue, size = 1.2) +
  labs(
    title = "Unemployment Duration",
    subtitle = "Median duration of unemployment (weeks)",
    caption = "Source: US Bureau of Labor Statistics"
  ) +
  scale_y_continuous(position = "right") + # Y轴放在右侧模仿 Economist (Y-axis on right)
  theme_economist_manual() +
  # 关键步骤:关闭裁剪并添加红标 (Key step: Turn off clipping and add tag)
  coord_cartesian(clip = "off") +
  annotation_custom(
      grob = rectGrob(gp = gpar(fill = econ_red, col = NA)), 
      xmin = -Inf, xmax = -Inf, 
      ymin = Inf, ymax = Inf
  ) + 
  # 微调红标位置 (Fine tune tag position using plot specific coordinates if needed, 
  # or better: use a fixed annotation outside)
  theme(plot.title = element_text(margin = margin(t = 20))) # Push title down

# 这里我们使用一个更加通用的 "Grobs" 方法来确保位置绝对正确
# Using a simpler Geom for the tag to guarantee visibility
p1_final <- p1 + 
  annotation_custom(
    grob = rectGrob(width = unit(2, "cm"), height = unit(1, "cm"), gp = gpar(fill = econ_red, col = NA), hjust = 0, vjust = 0), 
    xmin = min(df_line$date),  # Place at min X
    xmax = min(df_line$date), 
    ymin = max(df_line$uempmed) + 2, # Place above max Y
    ymax = max(df_line$uempmed) + 2
  )

# 让我们尝试最稳健的方法:grid 覆盖 (Let's try the most robust method: Grid overlay)
# 并在最后明确打印 (And explicitly print)

p1_base <- ggplot(df_line, aes(x = date, y = uempmed)) +
  geom_line(color = econ_blue, size = 1.2) +
  labs(title = "Unemployment Duration", subtitle = "Weeks", caption = "Source: BLS") +
  theme_economist_manual()

# 强制输出
print(p1_base)

# 注意:在网页中,我们用 grid.draw 叠加红标
# Note: In the web page, we overlay the red tag
grid.rect(x = 0.05, y = 0.95, width = 0.05, height = 0.025, just = c("left", "top"), gp = gpar(fill = econ_red, col = NA))

3. Bar Chart: Categorical Data (柱状图)

# 1. 准备数据
# Prepare data
df_bar <- mtcars %>%
  tibble::rownames_to_column("model") %>%
  head(8)

# 2. 绘图
# Plot
p2 <- ggplot(df_bar, aes(x = reorder(model, mpg), y = mpg)) +
  geom_col(fill = econ_blue, width = 0.7) +
  geom_text(aes(label = mpg), hjust = 1.5, color = "white", fontface = "bold") +
  coord_flip() +
  labs(
    title = "Fuel Efficiency",
    subtitle = "Miles per Gallon",
    caption = "Source: Motor Trend"
  ) +
  theme_economist_manual() +
  theme(panel.grid.major.y = element_blank())

# 3. Explicit Print and Tag
print(p2)
grid.rect(x = 0.05, y = 0.95, width = 0.05, height = 0.025, just = c("left", "top"), gp = gpar(fill = econ_red, col = NA))

4. Scatter Plot (散点图)

p3 <- ggplot(iris, aes(x = Sepal.Length, y = Petal.Length)) +
  geom_point(color = econ_blue, alpha = 0.6, size = 3) +
  geom_smooth(method = "lm", color = econ_red, size = 1, se = FALSE) +
  labs(
    title = "Iris Dimensions",
    subtitle = "Sepal vs Petal",
    caption = "Source: Fisher's Iris Data"
  ) +
  theme_economist_manual()

# Explicit Print
print(p3)
grid.rect(x = 0.05, y = 0.95, width = 0.05, height = 0.025, just = c("left", "top"), gp = gpar(fill = econ_red, col = NA))

Summary

We have used theme_economist_manual() to set the colors and grid.rect() with explicit print() to ensure the charts and their tags appear correctly.